////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package spark.components
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.utils.setTimeout;
	
	import mx.core.FlexGlobals;
	import mx.core.IFlexDisplayObject;
	import mx.managers.PopUpManager;
	
	import spark.components.SkinnablePopUpContainer;
	
	/**
	 *  The alpha of border stroke.
	 *
	 *  @default 0.65
	 *
	 *  @langversion 3.0
	 *  @playerversion AIR 3
	 *  @productversion Flex 4.6
	 */
	[Style(name = "strokeAlpha", type = "Number", inherit = "no", minValue = "0.0", maxValue = "1.0")]
	
	/**
	 *  The color of border stroke.
	 *
	 *  @default 0xFFFFFF
	 *
	 *  @langversion 3.0
	 *  @playerversion AIR 3
	 *  @productversion Flex 4.6
	 */
	[Style(name = "strokeColor", type = "uint", format = "Color", inherit = "no")]
	
	/**
	 *  The radius of the corners of the notification box.
	 *
	 *  @default 0
	 *
	 *  @langversion 3.0
	 *  @playerversion AIR 3
	 *  @productversion Flex 4.6
	 */
	[Style(name = "cornerRadius", type = "Number", inherit = "no", minValue = "0")]
	
	/**
	 *  The Spark Toast control defines lightweight feedback mechanism in a
	 *  small popup that automatically disappears after a set period of time.
	 * 	Toasts do not have any interactive controls and only display
	 * 	information to the user.
	 *
	 * 	A Toast consists of a box and a label.
	 *
	 *  @langversion 3.0
	 *  @playerversion AIR 3
	 *  @productversion Flex 4.6
	 *
	 *  @see spark.skins.mobile.ToastSkin
	 */
	public class Toast extends SkinnablePopUpContainer
	{
		public static const LONG_DELAY:int = 3500; // 3.5 seconds
		
		public static const SHORT_DELAY:int = 2000; // 2 seconds
		
		//----------------------------------------------------------------------------------------------
		//
		//  Variables
		//
		//----------------------------------------------------------------------------------------------
		
		/**
		 * Flag indicating if the toast is currently showing
		 */
		private var showing:Boolean;
		
		/**
		 * Internal timer for determining when toast should disappear
		 */
		private var popupTimer:Timer;
		
		//----------------------------------------------------------------------------------------------
		//
		//  Constructor
		//
		//----------------------------------------------------------------------------------------------
		
		/**
		 *  Constructor.
		 *
		 *  @langversion 3.0
		 *  @playerversion AIR 3
		 *  @productversion Flex 4.6
		 */
		public function Toast(label:String, duration:int = SHORT_DELAY, notificationParent:DisplayObject = null)
		{
			super();
			
			_label = label;
			_duration = duration;
			_notificationParent = notificationParent ? notificationParent : FlexGlobals.topLevelApplication.root;
			
			init();
		}
		
		//----------------------------------------------------------------------------------------------
		//
		//  Properties
		//
		//----------------------------------------------------------------------------------------------
		
		
		//----------------------------------
		//  label
		//----------------------------------
		
		private var _label:String;
		
		[Bindable(event="change")]
		/**
		 *
		 * @return
		 *  String
		 */
		public function get label():String
		{
			return _label;
		}
		
		public function set label(value:String):void
		{
			if (_label == value)
				return;
			
			_label = value;
			
			if (hasEventListener(Event.CHANGE))
				dispatchEvent(new Event(Event.CHANGE));
		}
		
		//----------------------------------
		//  duration
		//----------------------------------
		
		private var _duration:int;
		
		/**
		 * The duration in milliseconds that the notification should be visible
		 * to the user before disappearing.
		 *
		 * @return
		 *  The duration in milliseconds.
		 */
		public function get duration():int
		{
			return _duration;
		}
		
		/**
		 * @private
		 */
		public function set duration(value:int):void
		{
			if (_duration == value)
				return;
			
			_duration = value;
		}
		
		//----------------------------------
		//  notificationParent
		//----------------------------------
		
		private var _notificationParent:DisplayObject;
		
		/**
		 * The object that toasts should be displayed in. This allows toasts to
		 * be confined to areas where they have meaning or context.
		 *
		 * @return
		 * 	A DisplayObject.
		 */
		public function get notificationParent():DisplayObject
		{
			return _notificationParent;
		}
		
		//----------------------------------------------------------------------------------------------
		//
		//  Methods
		//
		//----------------------------------------------------------------------------------------------
		
		private function init():void
		{
			// create a timer to control visibility
			popupTimer = new Timer(_duration, 1);
			popupTimer.addEventListener(TimerEvent.TIMER, popupTimer_handler, false, 0, true);
		}
		
		/**
		 * Convenience method for creating a toast.
		 * @param label
		 * 	The label to display to the user
		 * @param duration
		 * 	The duration in milliseconds to display the notification. The default
		 * 	is SHORT_DELAY (2 seconds)
		 * @param parent
		 * 	The parent component to notify in. Default is the application.
		 * @return
		 * 	A created toast.
		 */
		public static function makeInstance(label:String, duration:int = SHORT_DELAY, parent:DisplayObject = null):Toast
		{
			return new Toast(label, duration, parent);
		}
		
		/**
		 * Show the toast to the user
		 */
		public function show():void
		{
			if (popupTimer.running)
				popupTimer.stop();
			
			// reset and restart our timer
			popupTimer.reset();
			popupTimer.start();
			
			// If we're not already showing then show it; otherwise, we're really
			// just extending the amount of time that we are shown
			if (!showing)
			{
				PopUpManager.addPopUp(this, _notificationParent);
				showing = true;
			}
		}
		
		/**
		 * Hide the toast from the user prior to it hiding itself
		 */
		public function hide():void
		{
			// stop our timer
			if (popupTimer.running)
				popupTimer.stop();
			
			// remove our notification
			if (showing)
			{
				PopUpManager.removePopUp(this);
				showing = false;
			}
		}
		
		/**
		 * Convenience method for tearing down the toast to a point where it
		 * can be garbage collected.
		 */
		public function dispose():void
		{
			if (showing)
			{
				PopUpManager.removePopUp(this);
				showing = false;
			}
			
			if (popupTimer)
			{
				if (popupTimer.hasEventListener(TimerEvent.TIMER))
					popupTimer.removeEventListener(TimerEvent.TIMER, popupTimer_handler, false);
				
				popupTimer.stop();
				popupTimer = null;
			}
		}
		
		//----------------------------------------------------------------------------------------------
		//
		//  Event Handlers
		//
		//----------------------------------------------------------------------------------------------
		
		protected function popupTimer_handler(event:TimerEvent):void
		{
			hide();
		}
		
	}
}